#include "includes.h"
#include "iocontroller.h"
#include "framework.h"
#include "coreMesh.h"
#include "camera.h"
#include "utils.h"
#include "scene.h"
#include "Instance.h"
#include <iostream> //to output
#include "controllerPerseguir.h"
#include "shader.h"
#include "clipper.h"
#include "pSystem.h"
#include "bass.h"
#include <conio.h>
#include "SoundManager.h"

//Function Definitions
void CargarSonidos();
void CargarEquipos();
void loadShaders();
void onDraw(bool first = false);
bool loadShaderVariables();
void init(void);
void loadMatrices(const Matrix44& model, const Matrix44& view, const Matrix44& projection);
void loadShaders();
void mainLoop();
void object(int var);
void onKeyPressed( SDL_KeyboardEvent event );
int main(int argc, char **argv);
void update(double seconds_elapsed);
int createWindow(const char* caption, int width, int height, bool fullscreen = false);
//******************************************************************

//Variable Definitions
Camera* camera; //our global camera

int objectSelected;
long last_time = 0; //this is used to calcule the elapsed time between frames
Uint8* keystate = NULL; //here we store the keyboard state
int mouse_last_x, mouse_last_y; //last mouse position
bool lock_mouse = false; //to block the mouse to navigate
Scene * scene;

IOController * ioc;
Clipper clipper;
// *********************************

// *********************************
Vector3 light_position[2];
Vector3 light_color[2];
//std::vector<Vector3> light_position;
//std::vector<Vector3> light_color;
//Colores;
Vector3 ambient_factor;
Vector3 diffuse_factor;
Vector3 specular_factor;
//Intensidades
float ambient_intensity;
float diffuse_intensity;
float specular_intensity;
//Alpha
float glossiness;
//Shader* shader = NULL;
// *********************************

int main(int argc, char **argv)
{
	if (createWindow("SpaceWars", WINDOW_WIDTH, WINDOW_HEIGHT,false) == 0)
		return 0;

	//useful functions
	//SDL_WarpMouse(300, 300); //move the mouse
	//SDL_WM_GrabInput( SDL_GRAB_ON ); //forces to keep the mouse by the app even when you change the focus
	//SDL_ShowCursor(true); //hides and shows the cursor
	
	init();
	mainLoop();

	//destroy everything and save
	//...

	return 0;
}
void init(void)
{
	//OpenGL flags
	glDisable( GL_CULL_FACE );
	glEnable( GL_DEPTH_TEST );
	loadShaders();
	scene = Scene::getScene();
	std::vector<std::string> textures;
	Vector3 v = Vector3(0,0,0);
	textures.push_back("loading");
	scene->LoadTextures(textures);

	onDraw(true);
	//CargarSonidos();

	scene->setCamera();
	ioc = new IOController();
	clipper = Clipper();

	Matrix44 * m;
	
	objectSelected=0;
	int Objetos = 1;
	char* name[1];
	name[0] = "x3_fighter";
	//name[2] = "space_train";
	//name[1] = "space_tank";

	//Cargamos SPACE_CUBEMAP
	std::string cubemap = "space_cubemap";
	textures.push_back(cubemap);
	m = new Matrix44();
	m->setIdentity();
	if(scene->LoadCore(cubemap)==NULL){
		std::cout <<"ERROR LOADING CORE " << cubemap << std::endl;
		exit(-1);
	}
	scene->NewInstance(cubemap, cubemap,Vector3(0,0,0), m,0);

	for (int i = 0; i < Objetos; i++){
		textures.push_back(name[i]);
		m = new Matrix44();
		m->setIdentity();
		if(scene->LoadCore(name[i]) == NULL){
			std::cout <<"ERROR LOADING CORE " << name[i] << std::endl;
			continue;
		}
	}

	m = new Matrix44();
	m->setIdentity();

	textures.push_back("asteroides");
	scene->NewInstance("asteroides", "asteroides", Vector3(0,0,0), m,2);
	textures.push_back("asteroides");
	scene->LoadCore("x3_fighter");
	scene->LoadCore("x3_runner");
	textures.push_back("x3_fighter");
	textures.push_back("x3_runner");
	textures.push_back("gauss");
	textures.push_back("gauss2");
	textures.push_back("gauss_noise");
	textures.push_back("explossion");
	textures.push_back("cruz");
	textures.push_back("hud-target");
	textures.push_back("engine");
	textures.push_back("ship-icon");
	textures.push_back("loading");
	textures.push_back("winner");
	textures.push_back("loser");
	scene->LoadTextures(textures);
	scene->setCamera();
	
	CargarEquipos();
	/*m = new Matrix44();
	m->setIdentity();
	scene->NewShip("space_train", "", Vector3(500,200,750), m, "space_train","shader","");
	m = new Matrix44();
	m->setIdentity();
	scene->NewShip("space_tank", "", Vector3(-500,-700,-750), m, "stace_tank","shader","");
	*/
	//player settings
	objectSelected = 0;
	Ship * ship = scene->getShipById(objectSelected);
	ship->isPlayer = true;
	ship->life = ship->life*2;
	ship->life_max = ship->life_max*2;
	ship->shield = ship->shield*2;
	ship->shield_max = ship->shield_max*2;
	
	//set the instance to follow the camera
	scene->getCamera()->folowAt(ship);
}
//The application main loop
void mainLoop()
{
	SDL_Event sdlEvent;

	while (1)
	{
		//render frame
		onDraw();

		//update events
		while(SDL_PollEvent(&sdlEvent))
		{
			switch(sdlEvent.type)
				{
					case SDL_QUIT: return; break; //EVENT for when the user clicks the [x] in the corner
					case SDL_MOUSEBUTTONDOWN: //EXAMPLE OF sync mouse input
						if (sdlEvent.button.button == SDL_BUTTON_MIDDLE)
						{
							lock_mouse = !lock_mouse;
							SDL_ShowCursor(!lock_mouse);
						}
						break;
					case SDL_MOUSEBUTTONUP:
						//...
						break;
					case SDL_KEYDOWN: //EXAMPLE OF sync keyboard input
						onKeyPressed( sdlEvent.key );
						break;
				}
		}

		//read keyboard state and stored in keystate
		keystate = SDL_GetKeyState(NULL);

		//update logic
		double elapsed_time = (SDL_GetTicks() - last_time) * 0.001; //0.001 converts from milliseconds to seconds
		last_time = SDL_GetTicks();
		update(elapsed_time); 
	}

	return;
}
//what to do when the image has to be draw
void onDraw(bool first) 
{

	glEnable( GL_TEXTURE_2D );
	//rtt->bind();
	//set the clear color (the background color)
	glClearColor(1.0, 1.0, 1.0, 1.0);
	// Clear the window and the depth buffer
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	if(first){
		Camera cam;
		cam.setOrthographic(-1,1,1,-1,0,1);
		cam.set();
		glEnable( GL_TEXTURE_2D );
		Scene::getScene()->DrawQuad(2,2,0,0,0,"loading");
	} else {
		glEnable( GL_TEXTURE_2D );

		//Put the camera matrices on the stack
		camera = scene->getCamera();
		camera->set();

		//Draw out world
		glDisable( GL_TEXTURE_2D ); //enable the textures 
		glColor3f(1,1,1);
		scene->renderScene();
	}
	
	//swap between front buffer and back buffer
	SDL_GL_SwapBuffers();
	glDisable( GL_TEXTURE_2D );
	
}
void update(double seconds_elapsed)
{
	//get mouse position and delta
	int x,y, mouse_state;
	mouse_state = SDL_GetMouseState(&x,&y);
	int delta_x = mouse_last_x - x;
	int delta_y = mouse_last_y - y;
	int player = objectSelected;
	mouse_last_x = x;
	mouse_last_y = y;
	double speed = seconds_elapsed * 200; //the speed is defined by the seconds_elapsed so it goes constant
	
	if(Scene::getScene()->estado_juego != EST_JUEGO) return;

	Ship * shipPlayer = scene->getShipPlayer();

	Matrix44 * object = shipPlayer->model;

	//to navigate with the mouse fixed in the middle
	if (lock_mouse)
	{
		SDL_WarpMouse(WINDOW_WIDTH*0.5, WINDOW_HEIGHT*0.5); //put the mouse back in the middle of the screen
		mouse_last_x = WINDOW_WIDTH*0.5;
		mouse_last_y = WINDOW_HEIGHT*0.5;
	}

	//mouse input to rotate the cam
	camera = scene->getCamera();
	if ((mouse_state & SDL_BUTTON_LEFT) || lock_mouse ) //is left button pressed?
	{
		camera->rotate(speed * delta_x * 0.001, Vector3(0,-1,0));
		camera->rotate(speed * delta_y * 0.001, camera->getLocalVector( Vector3(-1,0,0)));
	}
	float delta = 1 * speed;
	//async input to move the camera around
	JoystickState state = ioc->getJoystickState(0);
	
	//-----------------------------------
	//do something with joystick state!
	float angle_x, angle_y, angle_z;
	float cuanto = 120.0f;

	angle_x = state.axis[0]*speed/cuanto;
	angle_y = state.axis[1]*speed/cuanto;
	angle_z = state.axis[2]*speed/(cuanto-50);
	
	if(state.axis[0] > 0 )
		shipPlayer->Move("right", state.axis[0]*100,seconds_elapsed);
	if(state.axis[0] < 0 )
		shipPlayer->Move("left", -state.axis[0]*100,seconds_elapsed);
	if(state.axis[1] > 0 )
		shipPlayer->Move("up", state.axis[1]*100,seconds_elapsed);
	if(state.axis[1] < 0 )
		shipPlayer->Move("down", -state.axis[1]*100,seconds_elapsed);
	if(state.axis[2] > 0 )
		shipPlayer->Move("roll_right", state.axis[2]*100,seconds_elapsed);
	if(state.axis[2] < 0 )
		shipPlayer->Move("roll_left", -state.axis[2]*100,seconds_elapsed);


	 //Acelerar
	if(state.button[4])
	{
		std::cout<<"Frenar"<<std::endl;
		shipPlayer->Move("break",1,seconds_elapsed);
	}
	//Frenar
	if(state.button[5])
	{
		std::cout<<"Acelerar"<<std::endl;
		shipPlayer->Move("accelerate",1,seconds_elapsed);
	}
	//if (seconds_elapsed > 1.0) seconds_elapsed = 0;

	shipPlayer->Move("forward",100,seconds_elapsed);
	//object->traslateLocal(0.0f,0.0f,scene->getInstance(player,1)->speed);
	
	//-----------------------------------


	if (keystate[SDLK_UP]) shipPlayer->Move("accelerate",1,seconds_elapsed);
	if (keystate[SDLK_DOWN]) shipPlayer->Move("break",1,seconds_elapsed);

	if (keystate[SDLK_w])
		//scene->getInstance(player,1)->model->traslateLocal(0,0,-delta);
		shipPlayer->Move("down",100,seconds_elapsed);
	if (keystate[SDLK_s])
		//scene->getInstance(player,1)->model->traslateLocal(0,0,+delta);
		shipPlayer->Move("up",100,seconds_elapsed);
	if (keystate[SDLK_a])
		//scene->getInstance(player,1)->model->traslateLocal(-delta,0,0);
		shipPlayer->Move("left",100,seconds_elapsed);
	if (keystate[SDLK_d])
		//scene->getInstance(player,1)->model->traslateLocal(+delta,0,0);
		shipPlayer->Move("right",100,seconds_elapsed);
	if (keystate[SDLK_LEFT])
		//scene->getInstance(player,1)->model->traslateLocal(0,+delta,0);
		shipPlayer->Move("roll_left",100,seconds_elapsed);
	if (keystate[SDLK_RIGHT])
		//scene->getInstance(player,1)->model->traslateLocal(0,-delta,0);
		shipPlayer->Move("roll_right",100,seconds_elapsed);
	
	if (keystate[SDLK_SPACE]) fireTarget(player,-shipPlayer->model->frontVector(),1);
	if (keystate[SDLK_LCTRL]) fireTarget(player,-shipPlayer->model->frontVector(),3);
	
	/*
	if (keystate[SDLK_w]) camera->move(Vector3(0,0,1) * 3);
	if (keystate[SDLK_s]) camera->move(Vector3(0,0,-1) * 3);
	if (keystate[SDLK_a]) camera->move(Vector3(3,0,0));
	if (keystate[SDLK_d]) camera->move(Vector3(-3,0,0));
	*/
	//-----------------------------------------------------------------------------
	//        MOVEMOS LA CAMARA
	//-----------------------------------------------------------------------------
	
	/*
	std::string name_mesh = shipPlayer->id_mesh;
	Vector3 tmp = scene->loaded_meshes[name_mesh]->lejano - scene->loaded_meshes[name_mesh]->media;
	float offset = sqrt(tmp.x*tmp.x+tmp.y*tmp.y+tmp.z*tmp.z)+4.0f;
	camera->setOffset((Vector3(0,14,50).normalize() * offset * 2));
	Vector3 new_pos = *object * Vector3(0,0,0);
	Vector3 new_eye = *object * (Vector3(0,14,50).normalize() * offset * 2);
	Vector3 new_up  = object->rotateVector(Vector3(0,1,0));
	float a,b;

	a = 1 - offset/80;
	b = offset / 80;
	camera->eye = camera->eye * 0.89 + new_eye * 0.11;
	if(!lock_mouse) camera->center = new_pos;
	camera->up = new_up;
	camera->updateViewMatrix();
	camera->updateProjectionMatrix();
	*/

	camera->update();

	//update position of cubemap
	Matrix44 * box_model = scene->getInstance(0,0)->model;
	box_model->setPosition(camera->eye.x, camera->eye.y, camera->eye.z);

	scene->updateScene(seconds_elapsed);
	//-----------------------------------------------------------------------------
}
void object(int var)
{
	if (var == 1) {
		Matrix44 * m = new Matrix44();
		m->setIdentity();
		std::string coreid = scene->getInstance(objectSelected,1)->id_mesh;
		scene->NewShip(coreid, coreid, Vector3(0,0,0),m,coreid,"shader","red");
		std::cout<<"1 objeto mas"<<std::endl;
	}
	if (var == -1){
		//model.pop_back();
		std::cout<<"1 objeto menos"<<std::endl;
	}
}
//Keyboard event handler (sync input)
void onKeyPressed( SDL_KeyboardEvent event )
{
	switch(event.keysym.sym)
	{

		case SDLK_p:

			//REINICIAR PARTIDA
			Scene::reloadScene();
			init();

			break;
		case SDLK_F5:
			loadShaders();
			break;

		case SDLK_ESCAPE: exit(0);
	}
}
//create a window using SDL
int createWindow(const char* caption, int width, int height, bool fullscreen)
{
	int bpp = 0;
	int flags;

	if( SDL_Init(SDL_INIT_VIDEO) < 0)
	{
		fprintf(stderr, "Video initialization failed: %s\n", SDL_GetError());
		exit(-1);
	}
	SDL_EnableUNICODE(1);
	atexit(SDL_Quit);

	flags = SDL_OPENGL | ( fullscreen ? SDL_FULLSCREEN : 0);
	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); //or 24
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);

	//antialiasing (disable this lines if it goes too slow)
	SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
	SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4 ); //increase to have smoother polygons

	const SDL_VideoInfo* info = SDL_GetVideoInfo();

	if(!info)
	{
		fprintf(stderr, "Video query failed: %s\n", SDL_GetError());
		return 0;
	}

	bpp = info->vfmt->BitsPerPixel;
	if(SDL_SetVideoMode(width, height, bpp, flags) == 0)
	{
		fprintf(stderr, "Video mode set failed: %s\n", SDL_GetError());
		return 0;
	}

	SDL_PumpEvents(); //without this line asserts could fail on windows
	SDL_WM_SetCaption(caption,NULL);
	SDL_GetMouseState(&mouse_last_x,&mouse_last_y);
	return bpp;
}
void loadMatrices(const Matrix44& model, const Matrix44& view, const Matrix44& projection)
{
	//We upload the matrices into opengl
	glMatrixMode( GL_MODELVIEW );
	Matrix44 modelview;
	modelview = model * view;
	glLoadMatrixf( modelview.m );

	glMatrixMode( GL_PROJECTION );
	glLoadMatrixf( projection.m );

	glMatrixMode( GL_MODELVIEW );
}
void CargarSonidos(){

	SoundManager * SM = SoundManager::getSoundManager();
	
//	BASS_Init(1,44100,BASS_DEVICE_3D,0,0);
	BASS_Init(1,44100,0,0,0);

	//BASS_SetConfig(BASS_CONFIG_FLOATDSP, DWORD(true));
	int i = BASS_ErrorGetCode(); 
	//BASS_PluginLoad("sonidos/plugin/basswv.dll",0);
	//BASS_Set3DFactors(1.0,0.5,0.0);
	//BASS_Apply3D();

	
	SM->loadSound("sonidos/laser/gun_plasmafire.mp3","plasma_fire",1,0);
	i = BASS_ErrorGetCode(); 
	SM->loadSound("sonidos/laser/laser1.mp3","laser_fire",1,0);
	i = BASS_ErrorGetCode(); 
	SM->loadSound("sonidos/explosionNave.wav","ship_explossion",1,0);
	i = BASS_ErrorGetCode(); 
}
void loadShaders()
{
	Shader::init();
	Shader::Load("shaders/shader.vs","shaders/shader.ps");
	//Shader::ReloadAll();
	Shader::Load("shaders/frost.vs","shaders/frost.ps");

}
void CargarEquipos(){
	
	Matrix44 * m;
	int cantidadNaves = 1;
	std::string tipoNave;
	
	tipoNave = "x3_fighter";

	for(int i = 0; i < 10; i++){
		m = new Matrix44();
		m->setIdentity();
		if(i > cantidadNaves/2) tipoNave = "x3_runner";
		scene->NewShip(tipoNave,"equipos_pred",Vector3(100+(i*40),700,800),m,tipoNave,"shader","green");
	}
	tipoNave = "x3_fighter";
	for(int i = 0; i < cantidadNaves; i++){
		m = new Matrix44();
		m->setIdentity();
		if(i > cantidadNaves/2) tipoNave = "x3_runner";
		scene->NewShip(tipoNave,"equipos",Vector3(-300,100+(i*40),0),m,tipoNave,"shader","red");
	}
	tipoNave = "x3_fighter";
	for(int i = 0; i < cantidadNaves; i++){
		m = new Matrix44();
		m->setIdentity();
		if(i > cantidadNaves/2) tipoNave = "x3_runner";
		scene->NewShip(tipoNave,"equipos",Vector3(0,0,200+(i*40)),m,tipoNave,"shader","blue");
	}
	
}
bool loadShaderVariables(){

	return true;
}